package com.fla.puzzle.ui;

import junit.framework.Assert;

import com.danikula.aibolit.Aibolit;
import com.danikula.aibolit.annotation.InjectOnClickListener;
import com.danikula.aibolit.annotation.InjectService;
import com.fla.puzzle.CompletedManager;
import com.fla.puzzle.GameModelManager;
import com.fla.puzzle.PackManager;
import com.fla.puzzle.PuzzleApplication;
import com.fla.puzzle.R;
import com.fla.puzzle.pack.PackItem;
import com.fla.puzzle.ui.game.GameModel;
import com.fla.puzzle.ui.game.GameView;
import com.fla.puzzle.ui.game.GameView.OnGameFinishedListener;
import com.fla.puzzle.utils.AndroidUtils;
import com.fla.puzzle.utils.Utils;
import com.google.ads.AdRequest;
import com.google.ads.AdView;
import com.nostra13.universalimageloader.core.ImageLoader;

import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;

/**
 * Shows "Main Game" screen
 */
public class GameActivity extends BaseActivity {
    private static final String GAME_MODEL_KEY = GameActivity.class.getName() + "GAME_MODEL_KEY";
    private static final String RESHUFFLE_KEY = GameActivity.class.getName() + "RESHUFFLE_KEY";
    private static final String WAS_PAUSED_KEY = GameActivity.class.getName() + "WAS_PAUSED_KEY";
    
    private static final int TIME_BEFORE_START = 1500;

    private static final int GAME_OPTIONS_REQUEST_CODE = 1;
    private static final int LEVEL_SELECTOR_ACTIVITY_REQUESR_CODE = 2;

    @InjectService
    private CompletedManager completedManager;

    private GameModelManager gameModelManager = PuzzleApplication.getInstance().getGameModelManager();
    private PackManager packManager = PuzzleApplication.getInstance().getPackManager();

    private GameView gameView;
    private ImageView hintImage;
    private Button reshuffleButton;

    private GameModel gameModel;
    private PackItem nextPackItem;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Assert.assertTrue(getIntent().hasExtra(GAME_MODEL_KEY));

        Aibolit.setInjectedContentView(this, R.layout.main);

        gameView = (GameView) findViewById(R.id.gameView);
        hintImage = (ImageView) findViewById(R.id.hintImage);
        reshuffleButton = (Button) findViewById(R.id.reshuffleButton);
        final AdView adView = (AdView) findViewById(R.id.adView);

        adView.postDelayed(new Runnable() {
            @Override
            public void run() {
                adView.loadAd(new AdRequest());
            }
        }, 8000);

        boolean wasPaused = savedInstanceState != null && savedInstanceState.getBoolean(WAS_PAUSED_KEY);

        if (wasPaused && gameModelManager.restoreGame() != null) {
            setupGameModel(gameModelManager.restoreGame());
            gameModelManager.removeSavedGame();
        }
        else {
            setupGameModel((GameModel) getIntent().getSerializableExtra(GAME_MODEL_KEY));
        }

        if (getIntent().getBooleanExtra(RESHUFFLE_KEY, true) && !wasPaused) {
            reshuffle(TIME_BEFORE_START);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putBoolean(WAS_PAUSED_KEY, true);
        super.onSaveInstanceState(outState);
    }

    private void setupGameModel(GameModel gameModel) {
        this.gameModel = gameModel;

        PackItem currentPackItem = gameModel.getPackItem();
        this.nextPackItem = packManager.getNextPackItem(currentPackItem.getPackId(), currentPackItem.getId());

        initImageHint();

        gameView.setGameModel(gameModel);
        gameView.setOnGameFinishedListener(new GameFinishedHandler());
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (!gameView.isFinished()) {
            gameModelManager.saveGame(gameModel);
        }
        else {
            gameModelManager.removeSavedGame();
        }
    }

    /**
     * Starts {@link GameActivity}
     * 
     */
    public static void start(Context context, GameModel gameModel, boolean reshuffle) {
        Assert.assertNotNull(gameModel);

        Intent intent = new Intent(context, GameActivity.class);
        intent.putExtra(GAME_MODEL_KEY, gameModel);
        intent.putExtra(RESHUFFLE_KEY, reshuffle);
        context.startActivity(intent);
    }
    
    private void reshuffle(final int delay) {
        gameView.post(new Runnable() {
            @Override
            public void run() {
                new Shuffler(delay).execute();
            }
        });
    }

    private void reshuffle() {
        reshuffle(0);
    }

    private void initImageHint() {
        hintImage.setOnTouchListener(new HintImageDismisser());
        ImageLoader.getInstance().displayImage(Utils.toURI(gameModel.getPackItem().getImageId()).toString(), hintImage);
    }

    @InjectOnClickListener(R.id.reshuffleButton)
    private void options(View view) {
        OptionsActivity.startForResult(GameActivity.this, nextPackItem != null, gameModel.getPackItem(), true,
            GAME_OPTIONS_REQUEST_CODE);
    }

    @InjectOnClickListener(R.id.hintButton)
    private void hint(View view) {
        showOrHideHintImage(!isHintVisible());
    }

    @Override
    public void onBackPressed() {
        if (isHintVisible()) {
            showOrHideHintImage(false);
        }
        else {
            super.onBackPressed();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case GAME_OPTIONS_REQUEST_CODE:
                handleOptionsActivityResults(resultCode, data);
                break;
            case LEVEL_SELECTOR_ACTIVITY_REQUESR_CODE:
                handleChangeComplexityResults(resultCode, data);
                break;
            default:
                throw new IllegalArgumentException("No handler for request code: " + requestCode);
        }
    }

    private void handleChangeComplexityResults(int resultCode, Intent data) {
        if (resultCode == RESULT_OK) {
            int rowsCount = data.getIntExtra(LevelSelectorActivity.ROWS_COUNT_KEY, 0);
            int colsCount = data.getIntExtra(LevelSelectorActivity.COLS_COUNT_KEY, 0);

            gameModel = new GameModel(rowsCount, colsCount, gameModel.getPackItem());
            setupGameModel(gameModel);
            reshuffle();
        }
    }

    private void handleOptionsActivityResults(int resultCode, Intent data) {
        switch (resultCode) {
            case OptionsActivity.RESHUFFLE:
                reshuffle();
                break;
            case OptionsActivity.BACK_TO_PUZZLES:
                finish();
                break;
            case OptionsActivity.PLAY_NEXT:
                setupGameModel(new GameModel(gameModel.getRowsCount(), gameModel.getColsCount(), nextPackItem));
                reshuffle(TIME_BEFORE_START);
                break;
            case OptionsActivity.CHANGE_COMPLEXITY:
                LevelSelectorActivity.startForResult(this, gameModel.getPackItem(), LEVEL_SELECTOR_ACTIVITY_REQUESR_CODE);
                break;
        }
    }

    private void showOrHideHintImage(boolean show) {
        gameView.setVisibility(show ? View.GONE : View.VISIBLE);
        reshuffleButton.setEnabled(!show);

        hintImage.setVisibility(show ? View.VISIBLE : View.GONE);
    }

    private boolean isHintVisible() {
        return hintImage.getVisibility() == View.VISIBLE;
    }

    private void doShuffle() {
        int iterationCount = gameModel.getColsCount() * gameModel.getRowsCount() * 5;
        int delay = 3000 / iterationCount;

        gameView.shuffle(iterationCount, delay);
    }

    private class Shuffler extends AsyncTask<Void, Void, Void> implements OnCancelListener {
        private Dialog progress;
        private final int delay;
        
        public Shuffler(int delay) {
            this.delay = delay;
        }

        @Override
        protected void onPreExecute() {
            progress = new Dialog(GameActivity.this, R.style.TransparentDialog);
            progress.setCancelable(true);
            progress.setOnCancelListener(this);
            progress.show();
        }

        @Override
        protected Void doInBackground(Void... params) {
            SystemClock.sleep(delay);
            doShuffle();
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            AndroidUtils.safeDismiss(progress);
        }

        @Override
        public void onCancel(DialogInterface dialog) {
            AndroidUtils.safeDismiss(progress);
            cancel(true);
            finish();
        }
    }

    private class HintImageDismisser implements OnTouchListener {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            showOrHideHintImage(false);
            return true;
        }
    }

    private class GameFinishedHandler implements OnGameFinishedListener {
        @Override
        public void onFinished() {
            completedManager.markCompleted(gameModel.getPackItem());

            OptionsActivity.startForResult(GameActivity.this, nextPackItem != null, gameModel.getPackItem(), false,
                GAME_OPTIONS_REQUEST_CODE);
        }
    }
}